package com.csg.boot.starter.builder;

import com.csg.boot.starter.constant.SwaggerProperties;
import org.springframework.web.bind.annotation.RequestMethod;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.service.Parameter;
import springfox.documentation.service.SecurityScheme;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * @author george
 */
public class DocketBuilder implements BaseBuilder<Docket, SwaggerProperties.DocketProperties> {

    private static DocketBuilder builder = new DocketBuilder();

    private static ResponseMessageBuilder responseMessageBuilder = ResponseMessageBuilder.getInstance();

    private static ParameterBuilder parameterBuilder = ParameterBuilder.getInstance();

    private static SecuritySchemeBuilder securitySchemeBuilder = SecuritySchemeBuilder.getInstance();

    private static SecurityContextBuilder securityContextBuilder = SecurityContextBuilder.getInstance();

    private DocketBuilder() {
    }

    public static DocketBuilder getInstance() {
        return builder;
    }

    @Override
    public Docket build(SwaggerProperties.DocketProperties properties) {
        Docket docket = new Docket(DocumentationType.SWAGGER_2)

                .select()
                .apis(RequestHandlerSelectors.basePackage(properties.getBasePackage()))
                .paths(PathSelectors.any())
                .build()

                .pathMapping(properties.getPath())
                .apiInfo(apiInfo(properties.getApiInfo()))
                .forCodeGeneration(properties.getForCodeGen())
                .enable(properties.getExternallyConfiguredFlag())
                .useDefaultResponseMessages(properties.getApplyDefaultResponseMessages())
                .groupName(properties.getGroupName())
                .securityContexts(securityContexts(properties.getSecurity()))
                .securitySchemes(securitySchemes(properties.getSecurity()))
                .globalOperationParameters(globalOperationParameters(properties.getGlobalOperationParameters()))
                .ignoredParameterTypes(ignoredParameterTypes(properties.getIgnorableParameterTypes()));
        globalResponseMessage(docket, properties.getResponseMessages());
        return docket;
    }

    private void globalResponseMessage(Docket docket, Map<RequestMethod, List<SwaggerProperties.ResponseMessageProperties>> responseMessages) {
        responseMessages.forEach((key, value) -> docket.globalResponseMessage(key, responseMessageBuilder.build(value)));
    }

    private Class[] ignoredParameterTypes(Set<Class> ignorableParameterTypes) {
        return ignorableParameterTypes.toArray(new Class[ignorableParameterTypes.size()]);
    }

    private List<Parameter> globalOperationParameters(List<SwaggerProperties.ParametersProperties> globalOperationParameters) {
        return parameterBuilder.build(globalOperationParameters);
    }

    private List<? extends SecurityScheme> securitySchemes(SwaggerProperties.SecurityProperties properties) {
        return Collections.singletonList(securitySchemeBuilder.build(properties));
    }

    private List<SecurityContext> securityContexts(SwaggerProperties.SecurityProperties properties) {
        return Collections.singletonList(securityContextBuilder.build(properties));
    }

    private ApiInfo apiInfo(SwaggerProperties.ApiInfoProperties properties) {
        return new ApiInfoBuilder()
                .title(properties.getTitle())
                .description(properties.getDescription())
                .version(properties.getVersion())
                .termsOfServiceUrl(properties.getTermsOfServiceUrl())
                .contact(contact(properties.getContact()))
                .license(properties.getLicense())
                .licenseUrl(properties.getLicenseUrl())
                .build();
    }

    private Contact contact(SwaggerProperties.ContactProperties properties) {
        return new Contact(properties.getName(), properties.getUrl(), properties.getEmail());
    }

}
